package lrscp.lib.utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lrscp.lib.Log;

import org.apache.commons.io.FileUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class XmlUtils {
    public static void save(Object obj, String fileName) {
        XStream xstream = new XStream(new DomDriver());
        String xml = xstream.toXML(obj);
        try {
            File fd = new File(fileName);
            if (!fd.exists() && fd.getParentFile() != null) {
                fd.getParentFile().mkdirs();
            }
            Writer fw = new FileWriter(fileName);
            fw.write(xml);
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 
     * @param fileName
     * @return null if file doesn't exists
     */
    public static Object restore(String fileName) {
        XStream xstream = new XStream(new DomDriver());
        String xml;
        try {
            xml = FileUtils.readFileToString(new File(fileName));
            return xstream.fromXML(xml);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void createXml(String fileName, Document document) throws IOException {
        Writer fileWriter = new FileWriter(fileName);
        OutputFormat format = OutputFormat.createPrettyPrint();
        XMLWriter xmlWriter = new XMLWriter(fileWriter, format);
        xmlWriter.write(document);
        xmlWriter.close();
    }

    public static Document getDoc(String fileName) throws DocumentException {
        Document document = null;
        File inputXml = new File(fileName);
        if (!inputXml.exists() || !inputXml.isFile()) {
            return DocumentHelper.createDocument();
        }
        SAXReader saxReader = new SAXReader();
        document = saxReader.read(inputXml);
        return document;
    }

    public static Document createDoc() {
        return DocumentHelper.createDocument();
    }

    public static Element getElement(Document doc, String xPath) {
        XPath path = doc.createXPath(xPath);
        if (!path.booleanValueOf(doc)) {
            return null;
        }

        return (Element) path.selectSingleNode(doc);
    }

    public static List<Element> getElements(Document doc, String xPath) {
        Element e = getElement(doc, xPath);
        return e == null ? new ArrayList<Element>() : e.elements();
    }

    private static List<String> getPathSegments(String path) {
        ArrayList<String> segs = new ArrayList<String>();
        Matcher m = Pattern.compile("/([^/]+)").matcher(path);
        while (m.find()) {
            segs.add(m.group(1));
        }
        return segs;
    }

    public static Element createElements(Document doc, String absolutePath,
            boolean createLastElement) {
        Element e = getElement(doc, absolutePath);

        if (e != null) {
            if (!createLastElement) {
                return e;
            } else {
                List<String> segs = getPathSegments(absolutePath);
                // Log.i("createLastElement");
                return e.getParent().addElement(segs.get(segs.size() - 1));
            }
        }

        e = doc.getRootElement();
        List<String> segs = getPathSegments(absolutePath);

        if (e == null) {
            e = doc.addElement(segs.get(0));
        }

        // if root is different, we will failed
        if (!e.getName().equals(segs.get(0)))
            return null;

        for (int i = 1; i < segs.size(); i++) {
            Element e1 = e.element(segs.get(i));
            if (e1 != null) {
                e = e1;
                continue;
            }

            e = e.addElement(segs.get(i));
        }

        return e;
    }

    public static Element createElements(Document doc, String absolutePath, String text) {
        Element e = createElements(doc, absolutePath, false);
        e.setText(text);
        return e;
    }

    public static Element createElements(Document doc, String absolutePath, String text,
            boolean createLastElement) {
        Element e = createElements(doc, absolutePath, createLastElement);
        e.setText(text);
        return e;
    }

    /**
     * @param args
     * @throws DocumentException
     */
    public static void main(String[] args) throws DocumentException {
        Map<String, String> map = new HashMap<String, String>();

        map.put("a", "b");
        map.put("c", "d");
        save(map, "a.xml");
        Map m = (Map) restore("a.xml");
        Log.i(m.get("b"));
        // XmlUtils config = new XmlUtils();
        // Document doc;
        // try {
        // doc = getDoc("a.xml");
        // // Element e = doc.addElement("root1");
        // // e = e.addElement("accounts");
        // // e.addElement("account").setText("1");
        // // e.getParent().addElement("a").setText("2");
        // // Log.i(doc.getRootElement().element("accounts").getName());
        // // Log.i(getElement(doc, "roots").getName());
        // Element e = createElements(doc, "/root/h", false);
        // e = e.addElement("node");
        // e.setAttributeValue("name", "hello");
        // e.setText("hello");
        // XMLSerializer s = new XMLSerializer();
        // s.
        // // createElements(doc, "/root/a1/a2", "a", true);
        // // Log.i(getElement(doc, "/root/a1/a2").getText());
        // // e.addElement("test");
        // // Log.i(doc.selectNodes("/accounts").size());
        // // document.addElement("qname");
        // // document.add
        // createXml("a.xml", doc);
        // } catch (IOException e) {
        // e.printStackTrace();
        // }
    }
}
